package com.xc.tool.utils;


import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * <p>项目名称: Stars</p>
 * <p>描述: 扫描Class的工具类</p>
 * <P>创建人: 小程</p>
 * <P>创建人邮箱: 779943476@qq.com</p>
 * <p>创建时间: 2019/7/1</p>
 *
 * @version v1.0
 */
public class ScanClassUtils {

    /**
     * <p>功能描述: 根据注解筛选对应的class文件</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param packageName     包名
     * @param annotationClass 注解类
     * @param isRecursive     是否递归
     * @return 返回Class文件的集合
     */
    public static List<Class<?>> getClassList(String packageName, Class<? extends Annotation> annotationClass, boolean isRecursive) {
        List<Class<?>> classList = new ArrayList<Class<?>>();
        classList = getClassList(packageName, isRecursive);
        for (Class<?> clz : classList) {
            if (!clz.isAnnotationPresent(annotationClass)) {
                classList.remove(clz);
            }
        }
        return classList;
    }

    /**
     * <p>功能描述: 获取该包下的所有class文件</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param packageName 包名
     * @param isRecursive 是否递归
     * @return 返回Class文件的集合
     */
    public static List<Class<?>> getClassList(String packageName, boolean isRecursive) {
        List<Class<?>> classList = new ArrayList<Class<?>>();
        try {
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replace('.', '/'));
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (url != null) {
                    //拿到文件的协议
                    String protocol = url.getProtocol();
                    if ("file".equals(protocol)) {
                        String packagePath = url.getPath();
                        addClass(classList, packagePath, packageName, isRecursive);
                    } else if ("jar".equals(protocol)) { // 如果是jar包的情况
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        JarFile jarFile = jarURLConnection.getJarFile(); // 取到jar包下的文件
                        Enumeration<JarEntry> jarEntries = jarFile.entries();
                        while (jarEntries.hasMoreElements()) { //遍历jarEntries
                            JarEntry jarEntry = jarEntries.nextElement();//取到元素
                            String jarEntryName = jarEntry.getName(); //取到文件名
                            if (jarEntryName.endsWith(".class")) { //如果文件名以.class结尾，将对应的文件添加至集合中
                                String name = jarEntryName.substring(0, jarEntryName.lastIndexOf("."));
                                System.out.println(name);
                                String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");//取到类名

                                if (isRecursive || className.substring(0, className.lastIndexOf(".")).equals(packageName)) {
                                    classList.add(Class.forName(className));
                                }
                            }

                        }

                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return classList;

    }

    /**
     * <p>功能描述: 将所有.class文件放到List集合中</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param classList   存放Class文件的集合
     * @param packagePath 包路径
     * @param packageName 包名
     * @param isRecursive 是否递归
     * @return 子级包名
     */
    private static void addClass(List<Class<?>> classList, String packagePath,
                                 String packageName, boolean isRecursive) {
        File[] files = getClassFiles(packagePath);
        if (files != null) {
            for (File file : files) {
                String fileName = file.getName(); //Column.class
                if (file.isFile()) {
                    //取到对应的类名,这里的类名是权限定名，权限定名(绝对路径)
                    String className = getClassName(packageName, fileName);
                    //使用加载器获取类的class
                    try {
                        classList.add(Class.forName(className));
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                } else {
                    if (isRecursive) {
                        String subPackagePath = getSubPackagePath(packagePath, fileName);
                        String subPackageName = getSubPackageName(packageName, fileName);
                        //递归
                        addClass(classList, subPackagePath, subPackageName, isRecursive);
                    }
                }
            }

        }
    }

    /**
     * <p>功能描述: 获取子级包名</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param packageName 父级包名
     * @param folderName  下级文件夹名称
     * @return 子级包名
     */
    private static String getSubPackageName(String packageName, String folderName) {
        String subPackageName = folderName;
        if (packageName != null && (!"".equals(packageName))) {
            subPackageName = packageName + "." + subPackageName;
        }
        return subPackageName;
    }

    /**
     * <p>功能描述: 获取子目录路径</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1 </p>
     *
     * @param packagePath 包的路径
     * @param folderName  下级文件夹名称
     * @return 子目录的路径
     */
    private static String getSubPackagePath(String packagePath, String folderName) {
        String folderPath = folderName;
        if (packagePath != null && (!"".equals(packagePath))) {
            folderPath = packagePath + "/" + folderPath;
        }
        return folderPath;
    }

    /**
     * <p>功能描述: 根据包名和文件名获取类全名</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param packageName 包名
     * @param fileName    文件名
     * @return 类全名
     */
    private static String getClassName(String packageName, String fileName) {
        String className = fileName.substring(0, fileName.indexOf("."));
        if (packageName != null && !"".equals(packageName)) {
            className = packageName + "." + className;
        }
        return className;
    }

    /**
     * <p>功能描述: 过滤，获取路径下的.class文件和目录</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param packagePath 要获取路径
     * @return 文件数组
     */
    private static File[] getClassFiles(String packagePath) {
        //文件过滤器
        return new File(packagePath).listFiles(new FileFilter() {
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });
    }
}
